package edu.imac.angryfood.world;

import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.BodyDef;
import org.newdawn.slick.Image;
import org.newdawn.slick.SpriteSheet;

import edu.imac.angryfood.shapes.Shape;

/**
 * A single body in the world. A body holds a shape which collide with
 * the rest of the world. It also holds properties about the shapes once
 * they have been created
 */
public class Body {
	public void setShow(boolean show) {
		this.show = show;
	}

	/** The used body by JBox2D */
	private org.jbox2d.dynamics.Body jboxBody;
	/** Definition of the body used by JBox2D */
	protected BodyDef jboxBodyDef;
	/** True if this should be a static body */
	private boolean staticBody;
	/** The shape used to represent this body */
	private Shape shape;
	/** The userdata assigned to this body if any */
	private Object userData;
	/** Image of the object */
	private Image img;
	/** Does the object is drawn ? */
	private boolean show;
	/** Sprite Sheet of the body */
	private SpriteSheet images;
	/** life of the body */
	private int life;
	/** type of the body (BLOCK, PROJECTILE, STATIC) */
	private String type;
	/** the initialised of the body */
	private int initLife;
	
	/**
	 * Returns the image
	 * @return an image
	 */
	public Image getImg() {
		return img;
	}

	/**
	 * Apply the image
	 * @param img image
	 */
	public void setImg(Image img) {
		this.img = img;
	}
	
	/**
	 * Get the image at the position x, y
	 * @param x position in x
	 * @param y position in y
	 * @return the image
	 */
	public Image getImg(int x, int y){
		return  images.getSubImage(x, y);
	}
	
	/**
	 * Apply the image at the position x y
	 * @param x the position in x of the image
	 * @param y the position in y of the image
	 */
	public void setImg(int x, int y){
		this.img = images.getSubImage(x, y);
	}
	
	/**
	 * Create SpriteSheet, give to the body
	 * @param images create the body spritesheet with this spritesheet
	 */
	public void setSpriteSheet(SpriteSheet images){
		this.images = images;
	}
	
	/**
	 * Sets the type of body
	 * @param type type of the body
	 */
	public void setType(String type){
		this.type = type;
	}
	
	/**
	 * Get the type of the body
	 * @return the type of the body
	 */
	public String getType(){
		return type;
	}

	/**
	 * Constructor
	 * @param shape create the body with the shape
	 * @param x position in x of the body
	 * @param y position in y of the body
	 * @param img image of the body
	 */
	public Body(Shape shape, float x, float y, Image img) {
		this(shape,x,y,false,img);
	}

	/**
	 * Constructor
	 * @param shape create the body with the shape
	 * @param x position in x of the body
	 * @param y position in y of the body
	 * @param staticBody this body is static or not
	 * @param img image of the body
	 */
	public Body(Shape shape, float x, float y, boolean staticBody, Image img) {
		this(shape,x,y,staticBody);
		setImg(img);
	}

	/**
	 * Constructor
	 * @param shape create the body with the shape
	 * @param x position in x of the body
	 * @param y position in y of the body
	 * @param staticBody this body is static or not
	 * @param images spritesheet of the body
	 */
	public Body(Shape shape, float x, float y, boolean staticBody, SpriteSheet images) {
		this(shape,x,y,staticBody);
		setSpriteSheet(images);
		setImg(0,0);
	}

	/**
	 * Constructor
	 * @param shape create the body with the shape
	 * @param x position in x of the body
	 * @param y position in y of the body
	 */
	public Body(Shape shape, float x, float y) {
		this(shape,x,y,false);
	}

	/**
	 * Constructor
	 * @param shape create the body with the shape
	 * @param x position in x of the body
	 * @param y position in y of the body
	 * @param staticBody this body is static or not
	 */
	public Body(Shape shape, float x, float y, boolean staticBody) {
		jboxBodyDef = new BodyDef();
		jboxBodyDef.position = new Vec2(x,y);
		this.staticBody = staticBody;
		this.shape = shape;
		this.show = true;
	}
	
	/**
	 * Add the body to the world
	 * @param world the world
	 */
	public void addToWorld(World world) {
		org.jbox2d.dynamics.World jboxWorld = world.getJBoxWorld();
				
		setJboxBody(jboxWorld.createBody(jboxBodyDef));
		shape.createInBody(this);
		
		if (!staticBody) {
			getJboxBody().setMassFromShapes();
		} else {
			getJboxBody().m_type = org.jbox2d.dynamics.Body.e_staticType;
		}
	}

	/**
	 * Remove the body from the world
	 * @param world the world
	 */
	public void removeFromWorld(World world) {
		org.jbox2d.dynamics.World jboxWorld = world.getJBoxWorld();
		jboxWorld.destroyBody(getJboxBody());
	}
	
	/**
	 * Return true if it's static
	 * @return true or false
	 */
	public boolean isStatic() {
		return staticBody;
	}
	
	/**
	 * Return an object with all the informations Data of the body
	 * @return an object with all the informations Data of the body
	 */
	public Object getUserData() {
		return userData;
	}

	/**
	 * Set the data of the user
	 * @param object the object
	 */
	public void setUserData(Object object) {
		this.userData = object;
	}

	/**
	 * Return the Slick Shape
	 * @return the slick shape
	 */
	public org.newdawn.slick.geom.Shape getSlickShape() {
		return this.getShape().getSlickShape();
	}	
	
	/**
	 * Return the Shape
	 * @return the shape
	 */
	public Shape getShape() {
		return shape;
	}
	
	/**
	 * Sets the restitution
	 * @param rest the restitution 
	 */
	public void setRestitution(float rest) {
		shape.setRestitution(rest);
	}

	/**
	 * Sets the friction
	 * @param f the friction
	 */
	public void setFriction(float f) {
		shape.setFriction(f);
	}
	
	/**
	 * Sets the density
	 * @param den the density
	 */
	public void setDensity(float den) {
		shape.setDensity(den);
	}
	
	/**
	 * Sets the life
	 * @param life the life
	 */
	public void setLife(int life){
		this.life = life;
	}
	
	/**
	 * Gets the life
	 * @return the life
	 */
	public int getLife(){
		return this.life;
	}
	
	/**
	 * Loses life
	 * @param quantity of life to lose
	 */
	public void loseLife(int quantity){
		this.life = this.life - quantity;
	}

	/**
	 * Returns the JBoxBody
	 * @return the jboxbody
	 */
	public org.jbox2d.dynamics.Body getJboxBody() {
		return jboxBody;
	}
	
	/**
	 * Get the X position of the body
	 * @return The x position of the body
	 */
	public float getX() {
		checkBody();
		return getJboxBody().getPosition().x;		
	}
	
	/**
	 * Get the Y position of the body
	 * @return The y position of the body
	 */
	public float getY() {
		checkBody();
		return getJboxBody().getPosition().y;		
	}
	
	/**
	 * Gets the rotation of the body
	 * @return the body
	 */
	public float getRotation() {
		checkBody();
		return getJboxBody().getAngle();
	}
	
	/**
	 * Get the linear Velocity
	 * @return the linear velocity
	 */
	public Vec2 getLinearVelocity() {
		checkBody();
		return getJboxBody().getLinearVelocity();
	}

	/**
	 * Returns if the body is sleeping
	 * @return true or false
	 */
	public boolean isSleeping() {
		checkBody();
		return getJboxBody().isSleeping();
	}

	/**
	 * Apply a translatation to the body
	 * @param x
	 * @param y
	 */
	public void translate(float x, float y) {
		setPosition(getX()+x, getY()+y);
	}
	
	/**
	 * Check the body
	 */
	private void checkBody() {
		if (getJboxBody() == null) {
			throw new RuntimeException("This method requires that the body has been added to the world first");
		}
	} 

	/**
	 * Checks if it's out of bounds
	 * @return true or false
	 */
	public boolean isOutOfBounds() {
		checkBody();
		return getJboxBody().isFrozen();
	}
	
	/**
	 * Sets the position
	 * @param x
	 * @param y
	 */
	public void setPosition(float x, float y) {
		checkBody();
		getJboxBody().setXForm(new Vec2(x,y), getJboxBody().getAngle());
	}
	
	/**
	 * Sets the rotation
	 * @param rotation
	 */
	public void setRotation(float rotation) {
		checkBody();
		getJboxBody().setXForm(getJboxBody().getPosition(), rotation);
	}

	/**
	 * Sets the velocity
	 * @param xVelocity
	 * @param yVelocity
	 */
	public void setVelocity(float xVelocity, float yVelocity) {
		checkBody();
		Vec2 vel = getJboxBody().getLinearVelocity();
		vel.x = xVelocity;
		vel.y = yVelocity;
		getJboxBody().setLinearVelocity(vel);
	}

	/**
	 * Sets the Jbox Body
	 * @param jboxBody
	 */
	public void setJboxBody(org.jbox2d.dynamics.Body jboxBody) {
		this.jboxBody = jboxBody;
	}

	/**  
	 * Get the JBox2D body that is wrapped by this class 
	 */
	public org.jbox2d.dynamics.Body getJBoxBody() {
		return getJboxBody();
	}

	/**
	 * Shows the body
	 * @param show
	 */
	public void show(boolean show) {
		this.show = show;
	}

	/**
	 * Does the body is shown ?
	 * @return
	 */
	public boolean isShow() {
		return show;
	}

	/**
	 * Sets the initial life
	 * @param initLife
	 */
	public void setInitLife(int initLife){
		this.initLife = initLife;
	}
	
	/**
	 * Gets the initial life
	 * @return the initial life
	 */
	public int getInitLife(){
		return this.initLife;
	}
}
